import Canvas2DApplication from "../../src/Canvas2DApplication";
import ELayout from "../../src/enums/ELayout";
import Vector2 from "../../src/maths/Vector2";
import Size from "../../src/geom/Size";
import Rectangle from "../../src/geom/Rectangle";
import EImageFillType from "../../src/enums/EImageFillType";

type TextAlgin = 'start' | 'left' | 'center' | 'right' | 'end';

type TextBaseline = 'alphabetic' | 'hanging' | 'top' | 'middle' | 'bottom';

type FontType = '10px sans-serif' | '15px sans-serif' | '20px sans-serif' | '25 sans-serif';

type PatternRepeat = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';

type FontStyle = "normal" | "italic" | "oblique";

type FontVariant = "normal" | "small-caps";

type FontWeight = "normal" | "bold" | "bolder" | "lighter" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900";

type FontSize = "10px" | "12px" | "16px" | "18px" | "24px" | "50%" | "75%" | "100%" | "125%" | "150%" | "xx-small" | "x-small" | "small" | "medium" | "large" | "x-large" | "xx-large";

type FontFamily = "sans-serif" | "serif" | "courier" | "fantasy" | "monospace";

export default class TestApplication extends Canvas2DApplication {

    // 由于Colors独一无二，没有多个实例
    // 可以声明为公开的静态的数组类型
    public static Colors: string[] = [
        'aqua', // 浅绿色
        'black', // 黑色
        'blue', // 蓝色
        'fuchsia', // 紫红色
        'gray', // 灰色
        'green', // 绿色
        'lime', // 绿黄色
        'maroon', // 褐红色
        'navy', // 海军色
        'olive', // 橄榄色
        'orange', // 橙色
        'purple', // 紫色
        'red', // 红色
        'silver', // 银灰色
        'teal', // 蓝绿色
        'white', // 白色
        'yellow' // 黄色
    ];

    // 声明_lineDashOffset成员变量，初始化为0
    private _lineDashOffset: number = 0;
    // 线性渐变对象
    private _linearGradient!: CanvasGradient;

    private _radialGradient!: CanvasGradient;

    private _pattern: CanvasPattern | null = null;

    public constructor(canvas: HTMLCanvasElement) {
        super(canvas);
        this.addTimer(this.timeCallback.bind(this), 0.013);
    }

    public render(): void {
        // 由于canvas.getContext方法返回的CanvasRenderingContext2D可能会是null.因此vs code会强制要求 null 值检查，否则报错（tsconfig.json中设置为类型严格检查模式）
        if (this.context2D !== null) {
            // 可以通过CanvasRenderingContext2D获取Canvas元素，然后获取Canvas的尺寸大小
            // 流程1，总是在渲染其他形体前，先调用clearRect清屏

            this.context2D.clearRect(0, 0, this.context2D.canvas.width, this.context2D.canvas.height);


            // this._drawRect(10, 10, this.canvas.width - 20, this.canvas.height - 20);

            // this.printLineStates();
            // console.log('render');
            // this.strokeGrid();

        }
    }

    public start(): void {
        this.addTimer((id: number, data: any): void => {
            this.timeCallback(id, data);
        }, 0.033);
        super.start();
    }

    public timeCallback(id: number, data: any): void {
        // debugger;
        this.context2D!.clearRect(0, 0, this.context2D!.canvas.width, this.context2D!.canvas.height);
        this._updateLineDashOffset();
        this._drawLineDashRect(10, 10, this.canvas.width - 20, this.canvas.height - 20);
    }

    public fillLinearRect(x: number, y: number, w: number, h: number): void {
        if (this.context2D !== null) {
            // 每次要绘制时总是使用save/restore对
            this.context2D.save();
            // 如果当前的线性渐变对象为undefined，就生成一个。使用延迟创建方式，只有在用到时才创建一次
            if (this._linearGradient === undefined) {
                // 使用createLinearGradient
                // 创建从左到右水平线性渐变，也可以将参数改为（x+w,y,x,y）
                // 创建从右到左线性渐变
                this._linearGradient = this.context2D.createLinearGradient(x, y, x + w, y);
                // 下面代码创建从上到下线性渐变，也可以将参数改为(x,y+h,x,y)
                // 创建从下到上线性渐变
                // this._linearGradient = this.context2D.createLinearGradient(x, y, x, y + h);
                // 下面代码创建从左上角到右下角的线性渐变
                // this._linearGradient = this.context2D.createLinearGradient(x, y, x + w, y + h);
                // 下面代码创建从右下角到左上角的线性渐变
                // this._linearGradient = this.context2D.createLinearGradient(x + w, y + h, x, y);
                // 可以使用CanvasGradian对象的addColorStop添加5个color stop
                // 第一个参数是[0,1]的浮点数，可以将其当成线性颜色线所占的百分比
                // 第二个参数是一个字符串颜色值，下面使用了上一节中讲到的css颜色表示法
                this._linearGradient.addColorStop(0.0, 'grey');
                this._linearGradient.addColorStop(0.25, 'rgba(255,0,0,1)');
                this._linearGradient.addColorStop(0.5, 'green');
                this._linearGradient.addColorStop(0.75, '#0000FF');
                this._linearGradient.addColorStop(1.0, 'black');
                // 设置线性渐变对象
                this.context2D.fillStyle = this._linearGradient;
                // 这里使用rect方法直接绘制一个封闭的矩形对象
                // 前面代码使用moveTo和lineTo来实现矩形对象的绘制
                this.context2D.beginPath();
                this.context2D.rect(x, y, w, h);
                this.context2D.fill();
                // 恢复渲染状态
                this.context2D.restore();

            }
        }
    }

    public fillRadialRect(x: number, y: number, w: number, h: number): void {
        if (this.context2D !== null) {
            // 每次要绘制时总是使用save/restore对
            this.context2D.save();
            if (this._radialGradient === undefined) {
                // 计算矩形的中心点坐标
                let centX: number = x + w * 0.5;
                let centY: number = y + h * 0.5;
                // 矩形可能不是正方形，此时我们选择矩形的长度和宽度中最小的值作为直径
                let radius: number = Math.min(w, h);
                // 计算半径
                radius *= 0.5;
                // 放射渐变由内圆和外圆来定义
                // 可以调整一下内圆的半径来看一下效果
                // 提供内圆radius*0.1/0.3/0.8对比图
                this._radialGradient = this.context2D.createRadialGradient(centX, centY, radius * 0.3, centX, centY, radius);
                this._radialGradient.addColorStop(0.0, 'black');
                this._radialGradient.addColorStop(0.25, 'rgba(255,0,0,1)');
                this._radialGradient.addColorStop(0.5, 'green');
                this._radialGradient.addColorStop(0.75, '#0000FF');
                this._radialGradient.addColorStop(1.0, 'white');
            }
            this.context2D.fillStyle = this._radialGradient;
            this.context2D.fillRect(x, y, w, h);
            this.context2D.restore();
        }
    }

    public fillPatternRect(x: number, y: number, w: number, h: number, repeat: PatternRepeat = 'repeat'): void {
        if (this.context2D !== null) {
            if (this._pattern === null) {
                let img: HTMLImageElement = document.createElement('img') as HTMLImageElement;
                img.src = 'http://a2.att.hudong.com/36/48/19300001357258133412489354717.jpg';
                // img.src = imgUrl;
                img.onload = (evt: Event): void => {
                    if (this.context2D !== null) {
                        this._pattern = this.context2D.createPattern(img, repeat);
                        this.context2D.save();
                        if (this._pattern) {
                            this.context2D.fillStyle = this._pattern;
                        }
                        this.context2D.beginPath();
                        this.context2D.rect(x, y, w, h);
                        this.context2D.fill();
                        this.context2D.restore();
                    }
                };
            } else {
                this.context2D.save();
                this.context2D.fillStyle = this._pattern;
                this.context2D.beginPath();
                this.context2D.rect(x, y, w, h);
                this.context2D.fill();
                this.context2D.restore();
            }
        }
    }

    public fillCircle(x: number, y: number, radius: number, fillStyle: string | CanvasGradient | CanvasPattern = 'red'): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.fillStyle = fillStyle;
            this.context2D.beginPath();
            this.context2D.arc(x, y, radius, 0, Math.PI * 2);
            this.context2D.fill();
            this.context2D.restore();
        }
    }

    public fillRectangleWithColor(rect: Rectangle, color: string): void {
        if (rect.isEmpty()) {
            return;
        }
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.fillStyle = color;
            this.context2D.fillRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
            this.context2D.restore();
        }
    }

    public strokeLine(x0: number, y0: number, x1: number, y1: number): void {
        if (this.context2D !== null) {
            this.context2D.save();
            // this.context2D.strokeStyle = 'red';
            this.context2D.beginPath();
            this.context2D.moveTo(x0, y0);
            this.context2D.lineTo(x1, y1);
            this.context2D.stroke();
            this.context2D.closePath();
            this.context2D.restore();
        }
    }

    public strokeCoord(orginX: number, orginY: number, width: number, height: number): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.strokeStyle = 'red';
            this.strokeLine(orginX, orginY, orginX + width, orginY);
            this.context2D.restore();

            this.context2D.save();
            this.context2D.strokeStyle = 'blue';
            this.strokeLine(orginX, orginY, orginX, orginY + height);
            this.context2D.restore();
        }
    }

    public strokeRect(x: number, y: number, w: number, h: number, color: string = 'black'): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.strokeStyle = color;
            this.context2D.beginPath();
            this.context2D.moveTo(x, y);
            this.context2D.lineTo(x + w, y);
            this.context2D.lineTo(x + w, y + h);
            this.context2D.lineTo(x, y + h);
            this.context2D.closePath();
            this.context2D.stroke();
            this.context2D.restore();
        }
    }

    /**
     * *绘制网格
     * @param color     网格颜色
     * @param interval  网格大小
     */
    public strokeGrid(color: string = 'grey', interval: number = 10): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.strokeStyle = color;
            this.context2D.lineWidth = 0.5;
            for (let i: number = interval + 0.5; i < this.canvas.width; i += interval) {
                this.strokeLine(i, 0, i, this.canvas.height);
            }
            for (let i: number = interval + 0.5; i < this.canvas.height; i += interval) {
                this.strokeLine(0, i, this.canvas.width, i);
            }
            this.context2D.restore();
        }
        this.fillCircle(0, 0, 5, 'green');
        this.strokeCoord(0, 0, this.canvas.width, this.canvas.height);
    }

    public printTextStates(): void {
        if (this.context2D !== null) {
            console.log('****************TextStates*************');
            console.log('font : ' + this.context2D.font);
            console.log('textAlgin : ' + this.context2D.textAlign);
            console.log('textBaseline : ' + this.context2D.textBaseline);

        }
    }

    public fillText(text: string, x: number, y: number, color: string = 'white', align: TextAlgin = 'left', baseline: TextBaseline = 'top', font: FontType = '10px sans-serif'): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.textAlign = align;
            this.context2D.textBaseline = baseline;
            this.context2D.font = font;
            this.context2D.fillStyle = color;
            this.context2D.fillText(text, x, y);
            this.context2D.restore();
        }
    }

    public testCanvas2DTextLayout(): void {
        let x: number = 20;
        let y: number = 20;
        let width: number = this.canvas.width - x * 2;
        let height: number = this.canvas.height - y * 2;
        let drawX: number = x;
        let drawY: number = y;
        let radius: number = 3;

        this.fillRectWithTitle(x, y, width, height);
        this.fillText("left-top", drawX, drawY, 'white', 'left', 'top' /*, '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x + width;
        drawY = y;
        this.fillText("right-top", drawX, drawY, 'white', 'right', 'top' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x + width;
        drawY = y + height;
        this.fillText("right-bottom", drawX, drawY, 'white', 'right', 'bottom' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x;
        drawY = y + height;
        this.fillText("left-bottom", drawX, drawY, 'white', 'left', 'bottom' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x + width * 0.5;
        drawY = y + height * 0.5;
        this.fillText("center-middle", drawX, drawY, 'black', 'center', 'middle' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'red');

        drawX = x + width * 0.5;
        drawY = y;
        this.fillText("center-top", drawX, drawY, 'blue', 'center', 'top' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x + width;
        drawY = y + height * 0.5;
        this.fillText("right-middle", drawX, drawY, 'blue', 'right', 'middle' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x + width * 0.5;
        drawY = y + height;
        this.fillText("center-bottom", drawX, drawY, 'blue', 'center', 'bottom');
        this.fillCircle(drawX, drawY, radius, 'black');

        drawX = x;
        drawY = y + height * 0.5;
        this.fillText("left-middle", drawX, drawY, 'blue', 'left', 'middle' /* , '20px sans-serif' */);
        this.fillCircle(drawX, drawY, radius, 'black');
    }

    /**
     * *计算文本的宽高
     * @param text 
     * @param char 
     * @param scale 
     */
    public calcTextSize(text: string, char: string = 'W', scale: number = 0.5): Size {
        if (this.context2D !== null) {
            let size: Size = new Size();
            size.width = this.context2D.measureText(text).width;
            let w: number = this.context2D.measureText(char).width;
            size.height = w + w * scale;
            return size;
        }
        alert('context2D渲染上下文为null');
        throw new Error('context2D渲染上下文为null');
    }

    /**
     * 函数返回的类型是Rectangle，表示9个文本子矩形之一，这些子矩形是相对父矩形坐标系的表示
     * 这意味着父矩形原点为[0,0]，所以参数是父矩形的widthh和height，而没有x和y坐标
     * @param layout 
     * @param text 
     * @param parentWidth 父矩形的宽度
     * @param parentHeight 父矩形的高度
     */
    public calcLocalTextRectangle(layout: ELayout, text: string, parentWidth: number, parentHeight: number): Rectangle {
        // 计算出绘制文本的宽高
        let s: Size = this.calcTextSize(text);
        // 创建一个二维向量
        let o: Vector2 = Vector2.create();
        // 计算出当前文本子矩形左上角相对父矩形空间中的3个关键点（左上、中心、右下）坐标
        // 1.当前文本子矩形左上角相对父矩形左上角坐标，由于局部表示，所以为[0,0]
        let left: number = 0;
        let top: number = 0;
        // 2.当前文本子矩形左上角相对父矩形右下角坐标
        let right: number = parentWidth - s.width;
        let bottom: number = parentHeight - s.height;
        // 3.当前文本子矩形左上角相对父矩形中心点坐标
        let center: number = right * 0.5;
        let middle: number = bottom * 0.5;
        switch (layout) {
            case ELayout.LEFT_TOP:
                o.x = left;
                o.y = top;
                break;
            case ELayout.RIGHT_TOP:
                o.x = right;
                o.y = top;
                break;
            case ELayout.RIGHT_BOTTOM:
                o.x = right;
                o.y = bottom;
                break;
            case ELayout.LEFT_BOTTOM:
                o.x = left;
                o.y = bottom;
                break;
            case ELayout.CENTER_MIDDLE:
                o.x = center;
                o.y = middle;
                break;
            case ELayout.CENTER_TOP:
                o.x = center;
                o.y = top;
                break;
            case ELayout.RIGHT_MIDDLE:
                o.x = right;
                o.y = middle;
                break;
            case ELayout.CENTER_BOTTOM:
                o.x = center;
                o.y = bottom;
                break;
            case ELayout.LEFT_MIDDLE:
                o.x = left;
                o.y = middle;
                break;
        }
        return new Rectangle(o, s);
    }

    public fillRectWithTitle(x: number, y: number, width: number, height: number, title: string = '', layout: ELayout = ELayout.CENTER_MIDDLE, color: string = 'grey', showCoord: boolean = true): void {
        if (this.context2D !== null) {
            // 1.绘制矩形
            this.context2D.save();
            this.context2D.fillStyle = color;
            this.context2D.beginPath();
            this.context2D.rect(x, y, width, height);
            this.context2D.fill();
            // 如果有文字，根据枚举值计算x、y坐标
            if (title.length !== 0) {
                // 2.绘制文字信息
                // 在矩形的左上角绘制出相关文字信息，使用的是10px大小的文字
                // 调用calcLocalTextRectangle方法
                let rect: Rectangle = this.calcLocalTextRectangle(layout, title, width, height);
                // 绘制文本
                this.fillText(title, x + rect.origin.x, y + rect.origin.y, 'white', 'left', 'top', '10px sans-serif');
                // 绘制文本框
                this.strokeRect(x + rect.origin.x, y + rect.origin.y, rect.size.width, rect.size.height, 'rgba( 0 , 0 , 0 , 0.5 ) ');
                // 绘制文本框左上角坐标（相对父矩形表示）
                this.fillCircle(x + rect.origin.x, y + rect.origin.y, 2);
            }
            // 3.绘制变换的局部坐标系
            // 附加一个坐标系,x轴和y轴比矩形的width和heigth多20像素
            // 并且绘制3个像素的原点
            if (showCoord) {
                this.strokeCoord(x, y, width + 20, height + 20);
                this.fillCircle(x, y, 3);
            }
            this.context2D.restore();
        }
    }

    public makeFontString(size: FontSize = '10px', weight: FontWeight = 'normal', style: FontStyle = 'normal', variant: FontVariant = 'normal', family: FontFamily = 'sans-serif', ): string {
        let strs: string[] = [];
        strs.push(style);
        strs.push(variant);
        strs.push(weight);
        strs.push(size);
        strs.push(family);
        let ret: string = strs.join(" ");
        console.log(ret);
        return ret;
    }

    public loadAndDrawImage(url: string): void {
        let img: HTMLImageElement = document.createElement('img') as HTMLImageElement;
        img.src = url;
        img.onload = (ev: Event): void => {
            if (this.context2D !== null) {
                /* 
                                // 在console控制台输出载入图像的尺寸
                                // console.log(url + " 尺寸为 [ " + img.width + " , " + img.height + " ] ");
                                // 将srcImage保持原样的方式绘制到Canvas画布[ 10 , 10 ]的位置
                                this.context2D.drawImage(img, 10, 10);
                                // 将srcImage以拉伸缩放的方式绘制到Canvas画布指定的矩形中去
                                this.context2D.drawImage(img, img.width + 30, 10, 200, img.height);
                                // 将srcImage的部分区域[ 44 , 6 , 162 , 175 , 200 ]以拉伸缩放的方式绘制到Canvas画布指定的矩形[ 200 , img . height + 30 , 200 , 130 ]中去
                                this.context2D.drawImage(img, 44, 6, 162, 175, 200, img.height + 30, 200, 130); */

                this.drawImage(img, Rectangle.create(140, 20, 540, 300), Rectangle.create(44, 6, 162, 175), EImageFillType.STRETCH);
                this.drawImage(img, Rectangle.create(20, 20, 100, 50), Rectangle.create(44, 6, 162, 175), EImageFillType.REPEAT);
            }
        }
    }

    public drawImage(img: HTMLImageElement | HTMLCanvasElement, destRect: Rectangle, srcRect: Rectangle = Rectangle.create(0, 0, img.width, img.height), fillType: EImageFillType = EImageFillType.STRETCH): boolean {
        if (this.context2D === null) {
            return false;
        }

        if (srcRect.isEmpty()) {
            return false;
        }

        if (destRect.isEmpty()) {
            return false;
        }

        if (fillType === EImageFillType.STRETCH) {
            this.context2D.drawImage(img,
                srcRect.origin.x,
                srcRect.origin.y,
                srcRect.size.width,
                srcRect.size.height,
                destRect.origin.x,
                destRect.origin.y,
                destRect.size.width,
                destRect.size.height
            );
        } else {
            this.fillRectangleWithColor(destRect, 'grey');
            let rows: number = Math.ceil(destRect.size.width / srcRect.size.width);
            let colums: number = Math.ceil(destRect.size.height / srcRect.size.height);
            let left: number = 0;
            let top: number = 0;
            let right: number = 0;
            let bottom: number = 0;
            let width: number = 0;
            let height: number = 0;
            let destRight: number = destRect.origin.x + destRect.size.width;
            let destBottom: number = destRect.origin.y + destRect.size.height;
            if (fillType === EImageFillType.REPEAT_X) {
                colums = 1;
            } else if (fillType === EImageFillType.REPEAT_Y) {
                rows = 1;
            }

            for (let i: number = 0; i < rows; i++) {
                for (let j: number = 0; j < colums; j++) {
                    left = destRect.origin.x + i * srcRect.size.width;
                    top = destRect.origin.y + j * srcRect.size.height;

                    width = srcRect.size.width;
                    height = srcRect.size.height;
                    right = left + width;
                    bottom = top + height;
                    if (right > destRight) {
                        width = srcRect.size.width - (right - destRight);
                    }
                    if (bottom > destBottom) {
                        height = srcRect.size.height - (bottom - destBottom);
                    }
                    this.context2D.drawImage(img,
                        srcRect.origin.x,
                        srcRect.origin.y,
                        width,
                        height,
                        left, top, width, height
                    );
                }
            }
        }
        return true;
    }

    public getColorCanvas(amount: number = 32): HTMLCanvasElement {

        let step: number = 4;
        let canvas: HTMLCanvasElement = document.createElement('canvas') as HTMLCanvasElement;
        canvas.width = amount * step;
        canvas.height = amount * step;
        let context: CanvasRenderingContext2D | null = canvas.getContext("2d");
        if (context === null) {
            alert("离屏Canvas获取渲染上下文失败！");
            throw new Error("离屏Canvas获取渲染上下文失败！");
        }

        for (let i: number = 0; i < step; i++) {
            for (let j: number = 0; j < step; j++) {
                let idx: number = step * i + j;
                context.save();
                context.fillStyle = TestApplication.Colors[idx];
                context.fillRect(i * amount, j * amount, amount, amount);
                context.restore();
            }
        }

        return canvas;
    }

    public drawColorCanvas(): void {
        let colorCanvas: HTMLCanvasElement = this.getColorCanvas();
        this.drawImage(colorCanvas,
            Rectangle.create(100, 100, colorCanvas.width, colorCanvas.height));
    }

    public setShadowState(shadowBlur: number = 5, shadowColor: string = "rgba( 127 , 127 , 127 , 0.5 )", shadowOffsetX: number = 10, shadowOffsetY: number = 10): void {
        if (this.context2D !== null) {
            this.context2D.shadowBlur = shadowBlur;
            this.context2D.shadowColor = shadowColor;
            this.context2D.shadowOffsetX = shadowOffsetX;
            this.context2D.shadowOffsetY = shadowOffsetY;
        }
    }

    private _drawRect(x: number, y: number, w: number, h: number): void {
        if (this.context2D !== null) {
            // 流程2，每次要绘制时总是使用save/restore对
            this.context2D.save();
            // 流程3，在渲染状态save后，设置当前渲染状态
            this.context2D.fillStyle = 'grey';
            this.context2D.strokeStyle = 'blue';
            this.context2D.lineWidth = 2;

            // this.context2D.lineCap = 'butt';        // 默认
            // this.context2D.lineCap = 'round';    // 会在一条线段的两个端点处绘制一个半圆，其半径为lineWidth的一半
            // this.context2D.lineCap = 'square';      // 会在一条线段的两个端点处绘制一个矩形，该矩形的高度与lineWidth一致，宽度为lineWidth的一半

            // this.context2D.lineJoin = 'miter';      // 默认，在两条线段转接处（右上角和右下角）绘制一个四边形填充
            // this.context2D.lineJoin = 'bevel';   // 在两条线段连接处（右上角和右下角）绘制填充好的三角形
            // this.context2D.lineJoin = 'round';   // 在两条线段转接处（右上角和右下角）绘制填充好的圆弧

            // miterLimit属性，该属性只有在lineJoin属性的值为miter时才有意义。

            // 流程4，使用beginPath产生一个子路径
            this.context2D.beginPath();
            // 左手系，左手顺时针方向定义顶点坐标
            // 流程5，在子路径中添加向量点
            this.context2D.moveTo(x, y);     // moveTo到左上角
            this.context2D.lineTo(x + w, y)    // 绘制左上到右上线段
            this.context2D.lineTo(x + w, y + h)  // 绘制右上到右下线段
            this.context2D.lineTo(x, y + h);   // 绘制右下到左下线段
            // 流程6，如果是封闭形体，调用closePath方法
            // 要绘制封闭的轮廓边。因此调用closePath后会自动在起始点和结束点之间产生一条线连线
            this.context2D.closePath();
            // 流程7，如果是填充，请使用fill方法
            this.context2D.fill();
            // 流程8，如果是描边，请使用stroke方法
            this.context2D.stroke();
            // 恢复渲染状态
            this.context2D.restore();
        }
    }

    /**
     * 绘制虚线
     * @param x 
     * @param y 
     * @param w 
     * @param h 
     */
    private _drawLineDashRect(x: number, y: number, w: number, h: number): void {
        if (this.context2D !== null) {
            this.context2D.save();
            this.context2D.lineWidth = 2;
            //this . context2D . strokeStyle = 'blue' ;
            this.context2D.strokeStyle = 'rgb( 0 , 0 , 255 ) ';
            //this . context2D . strokeStyle = 'rgb( 0% , 0% , 100% ) ' ;
            //this . context2D . strokeStyle = 'rgb( 0% , 0% , 255 ) ' ;
            //this . context2D . fillStyle = "rgba( 0 , 255 , 0 , 0.5 )" ; 
            // if (this._linearGradient === undefined) {
            //     this._linearGradient = this.context2D.createLinearGradient(x, y, x + w, y);
            //     //this . _linearGradient = this . context2D . createLinearGradient ( x , y, x  , y + h ) ;
            //     //this . _linearGradient = this . context2D . createLinearGradient ( x , y, x + w , y + h ) ;
            //     //this . _linearGradient = this . context2D . createLinearGradient ( x + w , y + h , x  , y) ;
            //     this._linearGradient.addColorStop(0.0, 'rgba( 255 , 0 , 0 , 1 ) ');
            //     this._linearGradient.addColorStop(0.5, 'green');
            //     this._linearGradient.addColorStop(0.8, '#0000FF');
            //     this._linearGradient.addColorStop(1.0, 'black');
            // }

            // if (this._radialGradient === undefined) {
            //     let centX: number = x + w * 0.5;
            //     let centY: number = y + h * 0.5;
            //     let radius: number = Math.min(w, h);
            //     radius *= 0.5;
            //     this._radialGradient = this.context2D.createRadialGradient(centX, centY, radius * 0.2, centX, centY, radius);
            //     //this . _linearGradient = this . context2D . createLinearGradient ( x , y, x  , y + h ) ;
            //     //this . _linearGradient = this . context2D . createLinearGradient ( x , y, x + w , y + h ) ;
            //     this._radialGradient.addColorStop(0.0, 'rgba( 255 , 0 , 0 , 0.5 ) ');
            //     this._radialGradient.addColorStop(0.5, 'green');
            //     this._radialGradient.addColorStop(0.8, '#0000FF');
            //     this._radialGradient.addColorStop(1.0, 'black');
            // }

            // this.context2D.fillStyle = this._linearGradient;
            //this . context2D . fillStyle = this . _radiusGradient ;
            //this . context2D . lineDashOffset = 0 ;
            this.context2D.setLineDash([10, 5]);
            this.context2D.lineDashOffset = - this._lineDashOffset;
            // //this . context2D . lineDashOffset = 5 ;
            this.context2D.beginPath();
            this.context2D.moveTo(x, y);
            this.context2D.lineTo(x + w, y);
            this.context2D.lineTo(x + w, y + h);
            this.context2D.lineTo(x, y + h);
            this.context2D.closePath();
            // // this.context2D.fill();
            this.context2D.stroke();
            this.context2D.restore();
        }
    }

    /**
     * *打印描边的一些渲染属性
     */
    public printLineStates(): void {
        if (this.context2D !== null) {
            console.log('**********LineState**********');
            console.log(`lineWidth: ${this.context2D.lineWidth}`);
            console.log(`lineCap: ${this.context2D.lineCap}`);
            console.log(`lineJoin: ${this.context2D.lineJoin}`);
            console.log(`miterLimit: ${this.context2D.miterLimit}`);
        }
    }

    /**
     * 更新lineDashOffset的函数
     */
    private _updateLineDashOffset(): void {
        // 每次计时器回调时，更新1像素偏移量
        this._lineDashOffset++;
        // 如果偏移量操作10000像素（可以随意改），就再从0像素偏移量开始
        if (this._lineDashOffset > 10000) {
            this._lineDashOffset = 0;
        }
    }
}